package com.dynamic.sync.toolkit.mongo.sync.service.impl;

import com.dynamic.sync.toolkit.EnvType;
import com.dynamic.sync.toolkit.mongo.MongoClientProvider;
import com.dynamic.sync.toolkit.mongo.sync.service.SyncYzfReportSettingMongoService;
import com.mongodb.Block;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.collections4.CollectionUtils;
import org.bson.BsonDocument;
import org.bson.Document;
import org.springframework.stereotype.Component;

/**
 * @author ZonLen since on 2021/6/19 下午8:50
 */
@Component
public class SyncYzfReportSettingMongoServiceImpl implements SyncYzfReportSettingMongoService {

  private static final String COLLECTION_NAME = "YzfReportSetting";

  private final MongoDatabase sourceDb;

  private final MongoClientProvider mongoClientProvider;

  private List<Document> documents;

  private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(5, 10, 1,
      TimeUnit.MINUTES,
      new SynchronousQueue<>(),
      r -> new Thread(new ThreadGroup("SYNC-THREAD-GROUP"), r, "SYNC-THREAD"));

  public SyncYzfReportSettingMongoServiceImpl(MongoClientProvider mongoClientProvider) {
    this.mongoClientProvider = mongoClientProvider;
    sourceDb = mongoClientProvider.mongoDatabase(EnvType.prd);
  }

  private static final AtomicBoolean LOCK = new AtomicBoolean(true);

  @Override
  public void syncFromPrdForYzfReportSetting() {
    if (!LOCK.get()) {
      return;
    }
    try {
      LOCK.set(false);
      documents = documents(sourceDb);
//      THREAD_POOL_EXECUTOR.execute(()-> backUpTargetAndSyncFromPrd(EnvType.local));
      THREAD_POOL_EXECUTOR.execute(()-> backUpTargetAndSyncFromPrd(EnvType.dev));
      THREAD_POOL_EXECUTOR.execute(()-> backUpTargetAndSyncFromPrd(EnvType.test));
      THREAD_POOL_EXECUTOR.execute(()-> backUpTargetAndSyncFromPrd(EnvType.pre));
    } finally {
      LOCK.set(true);
    }
  }

  private void backUpTargetAndSyncFromPrd(EnvType envType) {
    final MongoDatabase targetDb = mongoClientProvider.mongoDatabase(envType);
    final String dateStr = LocalDateTime.now()
        .format(DateTimeFormatter.ofPattern("yyyy_MM_dd_HH"));
    final MongoCollection<Document> targetCollection = targetDb.getCollection(COLLECTION_NAME);
    final MongoCollection<Document> targetBackupsCollection = targetDb
        .getCollection(COLLECTION_NAME + dateStr);
    ifNotEmptyDelete(targetBackupsCollection);
    final List<Document> targetDocuments = documents(targetDb);
    if (CollectionUtils.isNotEmpty(targetDocuments)) {
      targetBackupsCollection.insertMany(targetDocuments);
    }
    ifNotEmptyDelete(targetCollection);
    if (CollectionUtils.isNotEmpty(documents)) {
      targetCollection.insertMany(documents);
    }
  }

  private void ifNotEmptyDelete(MongoCollection<Document> targetCollection) {
    final long count = targetCollection.countDocuments();
    if (count > 0) {
      targetCollection.deleteMany(new BsonDocument());
    }
  }

  private List<Document> documents(MongoDatabase database) {
    final List<Document> documents = new ArrayList<>();
    database.getCollection(COLLECTION_NAME).find().forEach(
        (Block<? super Document>) documents::add);
    return documents;
  }

}
